package com.sxf.redis;


import com.sxf.common.exceptions.InternalException;
import com.sxf.consts.Consts;
import com.sxf.redis.iredis.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.*;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *
 * @description: RedisUtil 工具类
 * @author: zhangyb@ccccit.com.cn
 * @date: 2021年1月5日 上午11:50:56
 */
public class RedisClient
		implements IDeleteRedisUtils, IExpireRedisUtils, IHashRedisUtils, IIncrementRedisUtils, IKeyRedisUtils,
		IListRedisUtils, ILockRedisUtils, ISetRedisUtils, IStringRedisUtils, ISortedSetRedisUtils, IListenerRedisUtils {

	private RedissonClient redissonClient;

	private String springApplicationName;

	private String env;

	public RedisClient(RedissonClient redissonClient, String springApplicationName, String env) {
		this.redissonClient = redissonClient;
		this.springApplicationName = springApplicationName;
		this.env = env;
	}

	private String generateKey(String nameSpace, String key) {

		if (StringUtils.isBlank(nameSpace)) {
			nameSpace = springApplicationName;
		}
		return new StringBuilder("{").append(nameSpace).append("_").append(env).append("}:").append(key).toString();
	}

	@Override
	public void set(String nameSpace, String key, Object value, long timeoutSeconds) {
		redissonClient.getBucket(generateKey(nameSpace, key)).set(value, timeoutSeconds, TimeUnit.SECONDS);
	}

	@Override
	public Object get(String nameSpace, String key) {
		return redissonClient.getBucket(generateKey(nameSpace, key)).get();
	}

	@Override
	public boolean addSet(String nameSpace, String key, long timeoutSeconds, Object[] values) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		rSet.expire(timeoutSeconds, TimeUnit.SECONDS);
		return rSet.addAll(Arrays.asList(values));
	}

	@Override
	public Set<Object> difference(String nameSpace, List<String> keys) {
		if (CollectionUtils.isEmpty(keys) || keys.size() < Consts.INT_NUM_2) {
			throw new InternalException("比较的keys不能为空");
		}

		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, keys.get(0)));
		String[] toBeComparedKeys = new String[keys.size() - 1];
		for (int keysNum = Consts.INT_NUM_1; keysNum < keys.size(); keysNum++) {
			toBeComparedKeys[keysNum - Consts.INT_NUM_1] = generateKey(nameSpace, keys.get(keysNum));
		}
		return rSet.readDiff(toBeComparedKeys);
	}

	@Override
	public Set<Object> difference(String nameSpace1, String key1, String nameSpace2, String key2) {
		if (StringUtils.isAnyBlank(key1, key2)) {
			throw new InternalException("相比较的两个key不能为空");
		}

		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace1, key1));
		return rSet.readDiff(generateKey(nameSpace2, key2));
	}

	@Override
	public boolean removeSet(String nameSpace, String key, Object[] values) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		return rSet.removeAll(Arrays.asList(values));
	}

	@Override
	public Boolean existKey(String nameSpace, String key) {
		return redissonClient.getKeys().countExists(generateKey(nameSpace, key)) > 0 ? true : false;
	}

	@Override
	public Iterable<String> getAllKey(String nameSpace, String keyPattern) {
		String pattern = generateKey(nameSpace, "") + "*" + (StringUtils.isEmpty(keyPattern) ? "" : (keyPattern + "*"));
		return redissonClient.getKeys().getKeysByPattern(pattern);
	}

	@Override
	public Long increment(String nameSpace, String key, long delta) {
		return redissonClient.getAtomicLong(generateKey(nameSpace, key)).addAndGet(delta);
	}

	@Override
	public void setHash(String nameSpace, String key, String hkey, Object value, long timeoutSeconds) {
		RMap<String, Object> rMap = redissonClient.getMap(generateKey(nameSpace, key));
		rMap.put(hkey, value);
		rMap.expire(timeoutSeconds, TimeUnit.SECONDS);
	}

	@Override
	public Object getHash(String nameSpace, String key, String hkey) {
		return redissonClient.getMap(generateKey(nameSpace, key)).get(hkey);
	}

	@Override
	public Set<Object> getHashKeys(String nameSpace, String key) {
		return redissonClient.getMap(generateKey(nameSpace, key)).readAllKeySet();
	}

	@Override
	public Map<Object, Object> multiGet(String nameSpace, String key, Set<Object> hkeys) {
		return redissonClient.getMap(generateKey(nameSpace, key)).getAll(hkeys);
	}

	@Override
	public Map<Object, Object> getValueMap(String nameSpace, String key) {
		return redissonClient.getMap(generateKey(nameSpace, key)).readAllMap();
	}

	@Override
	public Boolean expire(String nameSpace, String key, long timeoutSeconds) {
		return redissonClient.getKeys().expire(generateKey(nameSpace, key), timeoutSeconds, TimeUnit.SECONDS);
	}

	@Override
	public Long getExpire(String nameSpace, String key) {
		return redissonClient.getBucket(generateKey(nameSpace, key)).remainTimeToLive();
	}

	@Override
	public Long del(String nameSpace, String[] key) {
		if (key == null) {
			return (long) Consts.INT_NUM_0;
		}
		return redissonClient.getKeys().delete(Arrays.stream(key).map(delKey -> generateKey(nameSpace, delKey))
				.collect(Collectors.toList()).toArray(new String[] {}));
	}

	@Override
	public Long delHashByKey(String nameSpace, String key, Object[] hKeys) {
		return redissonClient.getMap(generateKey(nameSpace, key)).fastRemove(hKeys);
	}

	@Override
	public void lock(String nameSpace, String key, long leaseTimeSeconds) {
		RLock lock = redissonClient.getLock(generateKey(nameSpace, key));
		lock.lock(leaseTimeSeconds, TimeUnit.SECONDS);
	}

	@Override
	public boolean tryLock(String nameSpace, String key, long leaseTimeSeconds, long waitTimeSeconds) {
		RLock lock = redissonClient.getLock(generateKey(nameSpace, key));
		try {
			return lock.tryLock(waitTimeSeconds, leaseTimeSeconds, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			throw new InternalException("获取redis锁失败", e);
		}
	}

	@Override
	public boolean isLocked(String nameSpace, String key) {
		RLock lock = redissonClient.getLock(generateKey(nameSpace, key));
		return lock.isLocked();
	}

	@Override
	public void unlock(String nameSpace, String key) {
		RLock lock = redissonClient.getLock(generateKey(nameSpace, key));
		if (lock.isLocked()) {
			lock.unlock();
		}
	}

	@Override
	public Boolean forceUnlock(String nameSpace, String key) {
		RLock lock = redissonClient.getLock(generateKey(nameSpace, key));
		return lock.forceUnlock();
	}

	@Override
	public Integer addListener(String nameSpace, String key, ObjectListener objectListener) {
		return redissonClient.getBucket(generateKey(nameSpace, key)).addListener(objectListener);
	}

	@Override
	public Set<Object> intersection(String nameSpace, List<String> keys) {
		if (CollectionUtils.isEmpty(keys) || keys.size() < Consts.INT_NUM_2) {
			throw new InternalException("比较的keys不能为空");
		}

		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, keys.get(0)));
		String[] toBeComparedKeys = new String[keys.size() - 1];
		for (int keysNum = Consts.INT_NUM_1; keysNum < keys.size(); keysNum++) {
			toBeComparedKeys[keysNum - Consts.INT_NUM_1] = generateKey(nameSpace, keys.get(keysNum));
		}
		return rSet.readIntersection(toBeComparedKeys);
	}

	@Override
	public Set<Object> intersection(String nameSpace1, String key1, String nameSpace2, String key2) {
		if (StringUtils.isAnyBlank(key1, key2)) {
			throw new InternalException("相比较的两个key不能为空");
		}

		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace1, key1));
		return rSet.readIntersection(generateKey(nameSpace2, key2));
	}

	@Override
	public void clearSet(String nameSpace, String key) {
		redissonClient.getSet(generateKey(nameSpace, key)).clear();
	}

	@Override
	public boolean addSortedSet(String nameSpace, String key, long timeoutSeconds, Object[] values) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		expire(nameSpace, key, timeoutSeconds);
		return rsSet.addAll(Arrays.asList(values));
	}

	@Override
	public boolean removeSortedSet(String nameSpace, String key, Object[] values) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.removeAll(Arrays.asList(values));
	}

	@Override
	public void clearSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		rsSet.clear();
	}

	@Override
	public int sizeSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.size();
	}

	@Override
	public Collection<Object> readAllSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.readAll();
	}

	@Override
	public boolean isEmptySortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.isEmpty();
	}

	@Override
	public boolean isExistsSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.isExists();
	}

	@Override
	public Object firstSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.first();
	}

	@Override
	public Object lastSortedSet(String nameSpace, String key) {
		RSortedSet<Object> rsSet = redissonClient.getSortedSet(generateKey(nameSpace, key));
		return rsSet.last();
	}

	@Override
	public int sizeSet(String nameSpace, String key) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		return rSet.size();
	}

	@Override
	public Set<Object> readAllSet(String nameSpace, String key) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		return rSet.readAll();
	}

	@Override
	public boolean isEmptySet(String nameSpace, String key) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		return rSet.isEmpty();
	}

	@Override
	public boolean isExistsSet(String nameSpace, String key) {
		RSet<Object> rSet = redissonClient.getSet(generateKey(nameSpace, key));
		return rSet.isExists();
	}

	@Override
	public boolean addList(String nameSpace, String key, long timeoutSeconds, Object[] values) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.addAll(Arrays.asList(values));
	}

	@Override
	public boolean removeList(String nameSpace, String key, Object[] values) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.removeAll(Arrays.asList(values));
	}

	@Override
	public void clearList(String nameSpace, String key) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		rList.clear();
	}

	@Override
	public int sizeList(String nameSpace, String key) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.size();
	}

	@Override
	public boolean isEmptyList(String nameSpace, String key) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.isEmpty();
	}

	@Override
	public boolean isExistsList(String nameSpace, String key) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.isExists();
	}

	@Override
	public Object getListElement(String nameSpace, String key, int index) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.get(index);
	}

	@Override
	public List<Object> rangeList(String nameSpace, String key, int fromIndex, int toIndex) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.range(fromIndex, toIndex);
	}

	@Override
	public int indexOfList(String nameSpace, String key, Object value) {
		RList<Object> rList = redissonClient.getList(generateKey(nameSpace, key));
		return rList.indexOf(value);
	}

	@Override
	public void removeListener(String nameSpace, String key, int listenerId) {
		redissonClient.getBucket(generateKey(nameSpace, key)).removeListener(listenerId);
	}

	@Override
	public boolean setNx(String nameSpace, String key, Object value, long timeoutSeconds) {
		RBucket<Object> bucket = redissonClient.getBucket(generateKey(nameSpace, key));
		bucket.expire(timeoutSeconds, TimeUnit.SECONDS);
		return bucket.compareAndSet(null, value);
	}

}
